//
// mcast_sender.h
//

#ifndef KITTY_MCAST_SENDER_H
#define KITTY_MCAST_SENDER_H

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include "network.h"

namespace kitty {

class mcast_sender
{
public:
    mcast_sender(boost::asio::io_service& io_service,
        const boost::asio::ip::address& multicast_address,
        unsigned short multicast_port)
      : endpoint_(multicast_address, multicast_port),
        socket_(io_service, endpoint_.protocol())
    {
    }

    /// Serialize a message to multicast, say a heartbeat packet
    template <typename T, typename Handler>
    void async_write(const T& t, Handler handler)
    {
        // Serialize the data structure first so we know how large it is
        // TODO the size of the header and the data should be less than
        // kitty::mcast_max_length, which is the Ethernet MTU
        std::ostringstream archive_stream;
        boost::archive::text_oarchive archive(archive_stream);
        archive << t;
        std::string outbound_data = archive_stream.str();

        // Format the header
        std::ostringstream header_stream;
        header_stream << std::setw(kitty::mcast_header_length)
                      << std::hex << outbound_data.size();
        if (!header_stream ||
             header_stream.str().size() != kitty::mcast_header_length)
        {
            // If something goes wrong, inform the caller
            boost::system::error_code error(boost::asio::error::invalid_argument);
            socket_.io_service().post(boost::bind(handler, error, 0));
            return;
        }
        std::string outbound_header = header_stream.str();

        // The serialized header and data structure to be written to the socket
        std::vector<boost::asio::const_buffer> buffers;
        buffers.push_back(boost::asio::buffer(outbound_header));
        buffers.push_back(boost::asio::buffer(outbound_data));

        // To send both the header and the data in a single write operation
        socket_.async_send_to(buffers, endpoint_, boost::bind(handler,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
    }

private:
    /// The multicast endpoint
    boost::asio::ip::udp::endpoint endpoint_;

    /// The underlying socket
    boost::asio::ip::udp::socket   socket_;
};

} // namespace kitty

#endif // KITTY_MCAST_SENDER_H

